/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file erm_drv.c
 * @brief This file defines erm driver functions
 */

/*******Includes***************************************************************/
#include "erm_reg_access.h"
#include "clock_drv.h"
#include "flash_drv.h"

/*******Definitions************************************************************/
#define FLASH_PFLASH_SIZE_1M                        (0x400)
#define FLASH_PFLASH_SIZE_512K                      (0x200)

#define FLASH_PFLASH0_END_ADDRESS_1M                (0x1007ffff)
#define FLASH_PFLASH1_BASE_ADDRESS_1M               (0x10080000)
#define FLASH_PFLASH0_BASE_ADDRESS                  (0x10000000)
#define FLASH_PFLASH1_BASE_ADDRESS_512K             (0x10040000)
#define FLASH_PFLASH0_END_ADDRESS_512K              (0x1003ffff)


/*******Variables**************************************************************/
/*!@brief Table to save register base addresses for erm instances */
static ERM_Type* s_ermRegBase[CONFIG_ERM_INSTANCE_COUNT] = CONFIG_ERM_REG_BASE_PTRS;

#if (CONFIG_ERM_ALWAYS_CLOCK_ON == 0U)
/*!@brief Table to save erm clock names */
static module_names_t s_ermModuleName[CONFIG_ERM_INSTANCE_COUNT] = CONFIG_ERM_MODULE_NAMES;
#endif

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : ERM_Init
 *** Description   : Initializes the ERM module
 ******************************************************************************/
void ERM_Init(uint32_t instance, const erm_config_t* ermConfig)
{
    ASSERT_PARAM(instance < CONFIG_ERM_INSTANCE_COUNT);
    ASSERT_PARAM(ermConfig != NULL);

    ERM_Type* regBase = s_ermRegBase[instance];
#if (CONFIG_ERM_ALWAYS_CLOCK_ON == 0U)
    module_names_t ermModName = s_ermModuleName[instance];


    /* Set erm bus clock enable */
    CLOCK_SetModuleBusClockEnable(ermModName, true);
    /* Set erm instance in-active */
    CLOCK_SetModuleSoftResetState(ermModName, RESET_INACTIVE);
#endif

    ERM_REG_SetSramEccEventEnable(regBase, ERM_EVENT_SINGLE_BIT, ermConfig->enableSramSingleBitCorrectionInt);
    ERM_REG_SetSramEccEventEnable(regBase, ERM_EVENT_NON_CORRECTABLE, ermConfig->enableSramNonCorrectionInt);
    ERM_REG_SetFlashEccEventEnable(regBase, ERM_EVENT_SINGLE_BIT, ermConfig->enableFlashSingleBitCorrectionInt);
    ERM_REG_SetFlashEccEventEnable(regBase, ERM_EVENT_NON_CORRECTABLE, ermConfig->enableFlashNonCorrectionInt);
}

/*******************************************************************************
 *** Function Name : ERM_Deinit
 *** Description   : Deinitializes the ERM module
 ******************************************************************************/
void ERM_Deinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_ERM_INSTANCE_COUNT);

#if (CONFIG_ERM_ALWAYS_CLOCK_ON == 0U)
    module_names_t ermModName = s_ermModuleName[instance];

    /* Set erm module active */
    CLOCK_SetModuleSoftResetState(ermModName, RESET_ACTIVE);
#endif
}

/*******************************************************************************
 *** Function Name : ERM_GetUserConfig
 *** Description   : Gets ERM default configuration
 ******************************************************************************/
void ERM_GetUserConfig(uint32_t instance, erm_config_t* ermConfig)
{
    ASSERT_PARAM(instance < CONFIG_ERM_INSTANCE_COUNT);
    ASSERT_PARAM(ermConfig != NULL);

    ERM_Type* regBase = s_ermRegBase[instance];

    ermConfig->enableSramSingleBitCorrectionInt = ERM_REG_IsSramSinglebitIntEnable(regBase);
    ermConfig->enableSramNonCorrectionInt = ERM_REG_IsSramNonCorrectableIntEnable(regBase);
    ermConfig->enableFlashSingleBitCorrectionInt = ERM_REG_IsFlashSinglebitIntEnable(regBase);
    ermConfig->enableFlashNonCorrectionInt = ERM_REG_IsFlashNonCorrectableIntEnable(regBase);
}

/*******************************************************************************
 *** Function Name : ERM_ClearEvents
 *** Description   : Clears error event flag
 ******************************************************************************/
void ERM_ClearEvents(uint32_t instance, erm_ecc_event_t event)
{
    ASSERT_PARAM(instance < CONFIG_ERM_INSTANCE_COUNT);

    ERM_Type* regBase = s_ermRegBase[instance];

    if (event == ERM_EVENT_SINGLE_BIT) {
        ERM_REG_ClearSramSinglebitEccEvent(regBase);
        ERM_REG_ClearFlashSinglebitEccEvent(regBase);
    } else if (event == ERM_EVENT_NON_CORRECTABLE) {
        ERM_REG_ClearSramNonCorrectableEccEvent(regBase);
        ERM_REG_ClearFlashNonCorrectableEccEvent(regBase);
    } else {
        /*Nothing todo*/
    }
}
#if (CONFIG_ERM_FLASH_SEPARATED_ECC_CONTROL == 1U)
/*******************************************************************************
 *** Function Name : ERM_ExchangeErrorAddress
 *** Description   : Exchange the error address
 ******************************************************************************/
static uint32_t ERM_ExchangeErrorAddress(uint32_t errAddress)
{
    flash_block_swap_t pflashSwapStatus;
    uint16_t flashSize;
    uint16_t sramSize;

    pflashSwapStatus = FLASH_GetPflashSwapStatus();
    FLASH_GetChipMemorySizeInfo(&flashSize, &sramSize);
    if ((pflashSwapStatus == PFLASH_BLOCK_SWAPPING) && (errAddress != 0x0)) {
        if (flashSize == FLASH_PFLASH_SIZE_1M) {
            if (errAddress > FLASH_PFLASH0_END_ADDRESS_1M) {
                errAddress = (errAddress - FLASH_PFLASH1_BASE_ADDRESS_1M) + FLASH_PFLASH0_BASE_ADDRESS;
            } else {
                errAddress = (errAddress - FLASH_PFLASH0_BASE_ADDRESS) + FLASH_PFLASH1_BASE_ADDRESS_1M;
            }
        } else if (flashSize == FLASH_PFLASH_SIZE_512K) {
            if (errAddress > FLASH_PFLASH0_END_ADDRESS_512K) {
                errAddress = (errAddress - FLASH_PFLASH1_BASE_ADDRESS_512K) + FLASH_PFLASH0_BASE_ADDRESS;
            } else {
                errAddress = (errAddress - FLASH_PFLASH0_BASE_ADDRESS) + FLASH_PFLASH1_BASE_ADDRESS_512K;
            }
        }
    }

    return errAddress;
}
#endif
/*******************************************************************************
 *** Function Name : ERM_GetErrorDetail
 *** Description   : Gets the detail of the last ECC event in flash and ram
 ******************************************************************************/
void ERM_GetErrorDetail(uint32_t instance, erm_ecc_detail_t* eventDetail)
{
    ASSERT_PARAM(instance < CONFIG_ERM_INSTANCE_COUNT);
    ASSERT_PARAM(eventDetail != NULL);

    ERM_Type* regBase = s_ermRegBase[instance];

    /*Flash event*/
    if (ERM_REG_GetFlashNonCorrectableEccEvent(regBase)) {
        eventDetail->flashEvent = ERM_EVENT_NON_CORRECTABLE;
    } else if (ERM_REG_GetFlashSinglebitEccEvent(regBase)) {
        eventDetail->flashEvent = ERM_EVENT_SINGLE_BIT;
    } else {
        eventDetail->flashEvent = ERM_EVENT_NONE;
    }

    /*Sram event*/
    if (ERM_REG_GetSramNonCorrectableEccEvent(regBase)) {
        eventDetail->sramEvent = ERM_EVENT_NON_CORRECTABLE;
    } else if (ERM_REG_GetSramSinglebitEccEvent(regBase)) {
        eventDetail->sramEvent = ERM_EVENT_SINGLE_BIT;
    } else {
        eventDetail->sramEvent = ERM_EVENT_NONE;
    }

#if (CONFIG_ERM_FLASH_SEPARATED_ECC_CONTROL == 0U)
    /*Flash error detail*/
    eventDetail->flashEccSingleBitErrAddr = ERM_REG_GetFlashSinglebitAddr(regBase);
    eventDetail->flashNonCorrectErrAddr = ERM_REG_GetFlashNonCorrectableAddr(regBase);
#else
    /*Flash error detail*/
    eventDetail->pflashEccSingleBitErrAddr = ERM_REG_GetPflashSinglebitAddr(regBase);
    eventDetail->dflashEccSingleBitErrAddr = ERM_REG_GetDflashSinglebitAddr(regBase);
    eventDetail->pflashNonCorrectErrAddr = ERM_REG_GetPflashNonCorrectableAddr(regBase);
    eventDetail->dflashNonCorrectErrAddr = ERM_REG_GetDflashNonCorrectableAddr(regBase);

    eventDetail->pflashEccSingleBitErrAddr = ERM_ExchangeErrorAddress(eventDetail->pflashEccSingleBitErrAddr);
    eventDetail->pflashNonCorrectErrAddr = ERM_ExchangeErrorAddress(eventDetail->pflashNonCorrectErrAddr);

#endif
    /*Sram error detail*/
    eventDetail->sramEccSingleBitErrAddr = ERM_REG_GetSramSinglebitAddr(regBase);
    eventDetail->sramNonCorrectErrAddr = ERM_REG_GetSramNonCorrectableAddr(regBase);
}

/*******EOF********************************************************************/
